#include "record_desktop_x11.h"
#if defined (__linux__)  && !defined(__ANDROID__)
#include "../util/common.h"
#include "error_define.h"
#include "video.h"
#include <thread>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>

#pragma pack (1)
struct BITMAPFILEHEADER
{
    short    bfType;
    int    bfSize;
    short    bfReserved1;
    short    bfReserved2;
    int   bfOffBits;
};

struct BITMAPINFOHEADER
{
    int  biSize;
    int   biWidth;
    int   biHeight;
    short   biPlanes;
    short   biBitCount;
    int  biCompression;
    int  biSizeImage;
    int   biXPelsPerMeter;
    int   biYPelsPerMeter;
    int  biClrUsed;
    int  biClrImportant;
};

void saveXImageToBitmap(XImage *pImage)
{
    BITMAPFILEHEADER bmpFileHeader;
    BITMAPINFOHEADER bmpInfoHeader;
    FILE *fp;
    static int cnt = 0;
    int dummy;
    char filePath[255];
    memset(&bmpFileHeader, 0, sizeof(BITMAPFILEHEADER));
    memset(&bmpInfoHeader, 0, sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfType = 0x4D42;
    bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmpFileHeader.bfReserved1 = 0;
    bmpFileHeader.bfReserved2 = 0;
    int biBitCount =32;
    int dwBmpSize = ((pImage->width * biBitCount + 31) / 32) * 4 * pImage->height;
    printf("size of short:%d\r\n",(int)sizeof(short));
    printf("size of int:%d\r\n",(int)sizeof(int));
    printf("size of long:%d\r\n",(int)sizeof(long));
    printf("dwBmpSize:%d\r\n",(int)dwBmpSize);
    printf("BITMAPFILEHEADER:%d\r\n",(int)sizeof(BITMAPFILEHEADER));
    printf("BITMAPINFOHEADER:%d\r\n",(int)sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +  dwBmpSize;

    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfoHeader.biWidth = pImage->width;
    bmpInfoHeader.biHeight = -pImage->height;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = biBitCount;
    bmpInfoHeader.biSizeImage = 0;
    bmpInfoHeader.biCompression = 0;
    bmpInfoHeader.biXPelsPerMeter = 0;
    bmpInfoHeader.biYPelsPerMeter = 0;
    bmpInfoHeader.biClrUsed = 0;
    bmpInfoHeader.biClrImportant = 0;

    sprintf(filePath, "/home/hdd/testximage/bitmap%d.bmp", 0);
    fp = fopen(filePath,"wb");

    if(fp == NULL)
        return;

    fwrite(&bmpFileHeader, sizeof(bmpFileHeader), 1, fp);
    fwrite(&bmpInfoHeader, sizeof(bmpInfoHeader), 1, fp);
    fwrite(pImage->data, dwBmpSize, 1, fp);
    fclose(fp);
}

record_desktop_x11::record_desktop_x11()
{
    _imgBuf = nullptr;
    _draw_cursor = true;
}

record_desktop_x11::~record_desktop_x11()
{
    stop();
    clean_up();
}

int record_desktop_x11::init(const Rect& rect, const int fps)
{
    int error = AE_NO;
    if (_inited == true) {
        return error;
    }

    _fps = fps;
    _rect = rect;
    deskdisplay = XOpenDisplay(NULL);
    deskscreen  = DefaultScreen(deskdisplay);


    if (!(screen_image = XShmCreateImage(deskdisplay, DefaultVisual(deskdisplay, deskscreen),
                                         DefaultDepth(deskdisplay, deskscreen), ZPixmap, NULL, &shminfo,
                                         _rect.width(), _rect.height()))) {
        return false;
    }

    if ((shminfo.shmid = shmget(IPC_PRIVATE, screen_image->bytes_per_line * screen_image->height, IPC_CREAT | 0777))
            < 0) {
        return false;
    }

    if ((shminfo.shmaddr = screen_image->data = (char *)shmat(shminfo.shmid, 0, 0)) == (char *)-1) {
        return false;
    }

    shminfo.readOnly = false;
    if (!XShmAttach(deskdisplay, &shminfo)) {
        return false;
    }


    _imgBuf = (FRAME_PKG*)malloc(sizeof(FRAME_PKG));
    _imgBuf->w = rect.width();
    _imgBuf->h = rect.height();
    //_imgBuf->size = (rect.width() * 32 + 31) / 32 * rect.height() * 4;
    //_imgBuf->img = new uint8_t[_imgBuf->size];

    _inited = true;


    return error;
}

int record_desktop_x11::start()
{
    if (_running == true) {
        //al_warn("record desktop gdi is already running");
        return AE_NO;
    }

    if (_inited == false) {
        return AE_NEED_INIT;
    }

    _running = true;
    _thread = std::thread(std::bind(&record_desktop_x11::record_func, this));

    return AE_NO;
}

int record_desktop_x11::pause()
{
    _paused = true;
    return AE_NO;
}

int record_desktop_x11::resume()
{
    _paused = false;
    return AE_NO;
}

int record_desktop_x11::stop()
{
    _running = false;
    if (_thread.joinable())
        _thread.join();

    return AE_NO;
}

void record_desktop_x11::clean_up()
{
    _inited = false;

    if (_imgBuf) {
        //delete[] _imgBuf->img;
        free(_imgBuf);
    }
    _imgBuf = nullptr;
    XShmDetach(deskdisplay, &shminfo);
    XCloseDisplay(deskdisplay);
    XDestroyImage(screen_image);
}

//void record_desktop_x11::draw_cursor(HDC hdc)
//{
//	if (!(_ci.flags & CURSOR_SHOWING))
//		return;

//	//is cursor in the tartet zone
//	if (_ci.ptScreenPos.x < _rect.left() ||
//		_ci.ptScreenPos.x > _rect.right() ||
//		_ci.ptScreenPos.y < _rect.top() ||
//		_ci.ptScreenPos.y > _rect.bottom()
//		)
//		return;

//	HICON icon;
//	ICONINFO ii;

//	icon = CopyIcon(_ci.hCursor);
//	if (!icon)
//		return;

//	int dstx = 0, dsty = 0;
//	dstx = abs(_ci.ptScreenPos.x - _rect.left());
//	dsty = abs(_ci.ptScreenPos.y - _rect.top());

//	if (GetIconInfo(icon, &ii)) {
//		POINT pos;
//		DrawIconEx(hdc, dstx, dsty, icon, 0, 0, 0, NULL, DI_NORMAL);

//		DeleteObject(ii.hbmColor);
//		DeleteObject(ii.hbmMask);
//	}

//	DestroyIcon(icon);
//}

int record_desktop_x11::do_record()
{

    if(XShmGetImage(deskdisplay, RootWindow(deskdisplay, deskscreen), screen_image, _rect.x(), _rect.y(), AllPlanes))
    {
        //saveXImageToBitmap(screen_image);
        _imgBuf->w = screen_image->width;
        _imgBuf->h = screen_image->height;
        _imgBuf->size= (screen_image->width * 32 + 31) / 32 * screen_image->height * 4;
        _imgBuf->img = screen_image->data;
    }
    return  AE_NO;
}

void record_desktop_x11::record_func()
{
    int64_t pre_pts = 0;
    int64_t dur = AV_TIME_BASE / _fps;

    int ret = AE_NO;
    while (_running)
    {
        ret = do_record();
        if (ret != AE_NO) {
            //todo hdd
            //if (_on_error) _on_error(ret);
            break;
        }


        _on_data(_imgBuf);

        time_t pts =av_time();
        do_sleep(dur, pre_pts, pts);

        pre_pts = pts;
    }
}
#endif
